package com.hibase.core.timezone.util;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hibase.common.entity.User;
import com.hibase.common.exception.HibaseException;
import com.hibase.common.util.UserContext;
import com.hibase.core.timezone.annotation.HiBaseTimezone;
import com.hibase.core.timezone.constant.TimezoneContant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

/**
 * TODO
 *
 * @author EDZ
 * @version 1.0
 * @date 2020/10/13 11:29
 */
@Slf4j
@Component
public class TimezoneUtil {

    @Value("${hibase.timezone.user:8}")
    private Integer userTimezone;



    /**
     * 时区转换
     *
     * @param source         时间
     * @param nowTimeZone    eg:+8，0，+9，-1 等等
     * @param targetTimeZone 同nowTimeZone
     * @return
     */
    public static Date timeZoneTransfer(Date source, Integer nowTimeZone, Integer targetTimeZone) {
        if (ObjectUtil.isEmpty(source)) {
            return source;
        }
        Date middleDate = DateUtil.offset(source, DateField.HOUR, -nowTimeZone);
        DateTime result = DateUtil.offset(middleDate, DateField.HOUR, targetTimeZone);
//        log.debug("时区转换 原时间 {} ,原时区 UTC{} , 目标时区 UTC{} , 目标时间: {}",
//                DateUtil.format(source,DatePattern.NORM_DATETIME_FORMAT),nowTimeZone,targetTimeZone,
//                DateUtil.format(result,DatePattern.NORM_DATETIME_FORMAT));
        return result;
    }

    /**
     * 时区转换,将仓库时间转换为系统时间
     *
     * @param source         时间
     * @param nowTimeZone    eg:  UTC8  , UTC-8
     * @param targetTimeZone 同nowTimeZone
     * @return
     */
    public static Date timeZoneTransfer(Date source, String nowTimeZone, String targetTimeZone) {
        if (ObjectUtil.isEmpty(source)) {
            return source;
        }
        Date middleDate = DateUtil.offset(source, DateField.HOUR, -TimezoneUtil.getTimeZone(nowTimeZone));
        DateTime result = DateUtil.offset(middleDate, DateField.HOUR, TimezoneUtil.getTimeZone(targetTimeZone));
        log.info("时区转换 原时间 {} ,原时区 {} , 目标时区 {} , 目标时间: {}",
                DateUtil.format(source,DatePattern.NORM_DATETIME_FORMAT),nowTimeZone,targetTimeZone,
                DateUtil.format(result,DatePattern.NORM_DATETIME_FORMAT));
        return result;
    }



    /**
     * 时区转换 月底日期上注解有HibaseJsonFormat才能转换
     *
     * @param list
     * @return
     */
    public void handleTimeZoneByList(List<Object> list, Integer source, Integer target) {
        list.forEach(o -> {
            handleTimeZoneByDetail(o, source, target);
        });
    }

    /**
     * 时区转换 月底日期上注解有HibaseJsonFormat才能转换
     *
     * @param o
     * @return
     */
    public void handleTimeZoneByDetail(Object o, Integer source, Integer target) {
        if (ObjectUtil.isNotEmpty(o)) {
            Field[] fields = o.getClass().getDeclaredFields();
            for (Field field : fields) {
                // 此注解只能注解在日期上
                boolean hasEnumField = field.isAnnotationPresent(HiBaseTimezone.class);
                if (hasEnumField) {
                    field.setAccessible(true);
                    Object oldValue = null;
                    try {
                        oldValue = field.get(o);
                    } catch (IllegalAccessException e) {
                        log.error("=================日期转换错误！msg:{}================",e.getMessage());
                    }
                    //2.更新时间
                    if (ObjectUtil.isNotEmpty(oldValue)) {
                        if(oldValue instanceof Date){
                            Date date = TimezoneUtil.timeZoneTransfer((Date) oldValue, source, target);
                            log.debug("时区转换: 字段 {}, 原时间 {}, 转换后时间 {}",field.getName(),
                                    DateUtil.format((Date) oldValue,DatePattern.NORM_DATETIME_FORMAT),
                                    DateUtil.format(date,DatePattern.NORM_DATETIME_FORMAT));
                            try {
                                field.set(o, date);
                            }catch (IllegalAccessException e) {
                                log.error("=================日期转换错误！msg:{}================",e.getMessage());
                            }

                        }
                        else {
                            throw new HibaseException("只支持日期格式的进行时区转换");
                        }
                    }
                }
            }
        }
    }

    /**
     * 获得当前用户时区
     *
     * @return
     */
    public String getCurrentTimeZone() {
        //step 01 默认 UTC0
        String timezone;
        //step 02 从请求头中取
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        timezone = request.getHeader(TimezoneContant.HEADER);
        if (StrUtil.isNotBlank(timezone)) {
            log.debug("通过请求头获得当前用户时区 {}", timezone);
            return timezone;
        }
        //step 03 从缓存中中取
        User user = UserContext.get();
        if (ObjectUtil.isNotEmpty(user)) {
            timezone = user.getTimezone();
            if (StrUtil.isNotBlank(timezone)) {
                log.info("通过缓存中获得当前用户时区 {}", timezone);
                return timezone;
            }
        }
        //默认
        timezone = TimezoneContant.PREFIX + userTimezone;
        log.info("获得当前用户时区 {}", timezone);
        return timezone;
    }


    /**
     * 时区转换  系统时区转换为 目标时区
     *
     * @param time           时间
     * @param targetTimeZone 同nowTimeZone   UTC-8
     * @return
     */
    public static Date timeZoneTransfer(Date time, String targetTimeZone) {
        if (ObjectUtil.isEmpty(time)) {
            return time;
        }
        Integer source = getSystemTimeZone(TimeZone.getDefault().getID());
        Integer target = getTimeZone(targetTimeZone);
        Date date = timeZoneTransfer(time, source, target);
        return date;
    }

    /**
     * 时区转换  原时区 转换为 系统时区
     *
     * @param time           时间
     * @param sourceTimeZone 同nowTimeZone UTC-8
     * @return
     */
    public static Date timeZoneTransferToSysTimeZnoe(Date time, String sourceTimeZone) {
        if (ObjectUtil.isEmpty(time)) {
            return time;
        }
        Integer source = getTimeZone(sourceTimeZone);
        Integer target = getSystemTimeZone(TimeZone.getDefault().getID());
        Date date = timeZoneTransfer(time, source, target);
        return date;
    }

    /**
     * 得到当前时间偏移量
     */
    public static Integer getSystemTimeZone(String id) {
        Integer result = 0;
        TimeZone timeZone = TimeZone.getTimeZone(id);
        Calendar calendar = Calendar.getInstance(timeZone);
        int milliseconds = calendar.get(Calendar.ZONE_OFFSET);
        if (milliseconds != 0) {
            result = milliseconds / (60 * 60 * 1000);
        }
        log.info("当前系统时区时区:  UTC{}",result);
        return result;
    }

    /**
     * 时区去头
     */
    public static Integer getTimeZone(String timeZone) {
        String result = StrUtil.removePrefix(timeZone, TimezoneContant.PREFIX);
        return Integer.valueOf(result);
    }

    /**
     * 与当前环境的偏移值
     */
    public static Integer getOffset(String timeZone) {
        Integer zone = getTimeZone(timeZone);
        Integer systemTimeZone = getSystemTimeZone(TimeZone.getDefault().getID());
        return systemTimeZone - zone;
    }

    /**
     * locateDateTime 转 Date
     *
     * @param localDateTime
     * @return
     */
    public static Date localDateTime2Date(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);//Combines this date-time with a time-zone to create a  ZonedDateTime.
        return Date.from(zdt.toInstant());

    }
}
